#!/usr/bin/python3

# This file is part of Cockpit.
#
# Copyright (C) 2013 Red Hat, Inc.
#
# Cockpit is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
# Cockpit is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Cockpit; If not, see <http://www.gnu.org/licenses/>.

import parent
from testlib import *


class TestTerminal(MachineCase):

    @skipBrowser("Firefox needs http to access clipboard", "firefox")
    def testBasic(self):
        b = self.browser
        m = self.machine
        b.default_user = "admin"

        # Make sure we get what we expect
        m.write("/tmp/bashrc-extra", """
PS1="\\u@\\h \\W]\$ "
PROMPT_COMMAND='printf "\\033]0;%s@%s:%s\\007" "${USER}" "${HOSTNAME%%.*}" "${PWD/#$HOME/\~}"'
""")
        m.execute("cat /tmp/bashrc-extra >>/home/admin/.bashrc")

        self.login_and_go("/system/terminal")

        blank_state = ' '
        if m.image == "rhel-8-2-distropkg":
            blank_state = 'Blank line'

        def line_sel(i):
            return '.terminal .xterm-accessibility-tree div:nth-child(%d)' % i

        def wait_line(i, t):
            b.wait_text(line_sel(i), t)

        # wait until first line is not empty
        n = 1
        b.wait_present(".terminal .xterm-accessibility-tree")
        function_str = "(function (sel) { return ph_text(sel).trim() != '%s'})" % blank_state
        b.wait_js_func(function_str, line_sel(n))

        # clear any messages (for example, instructions about sudo) and wait for prompt
        b.key_press("clear")
        b.wait_js_cond("ph_text('.terminal').indexOf('clear') >= 0")
        # now wait for clear to take effect
        b.key_press("\r")
        b.wait_js_cond("ph_text('.xterm-accessibility-tree').indexOf('clear') < 0")
        # now we should get a clean prompt
        b.wait_in_text(line_sel(n), '$')

        # cut trailing non-breaking spaces
        prompt = b.text(line_sel(n))

        # Make sure we are started in home directory
        # Account for non-standard prompting
        if "]" not in prompt:
            self.assertIn(":~$", prompt)
        else:
            self.assertIn("~]$", prompt)

        # Run some commands
        b.key_press("whoami\r")
        wait_line(n + 1, "admin")

        wait_line(n + 2, prompt)

        b.key_press('echo -e "1\\u0041"\r')
        wait_line(n + 3, '1A')

        # The '@' sign is in the default prompt
        b.wait_in_text(".terminal-title", '@')

        # now reset terminal
        b.click('.btn:contains("Reset")')

        # assert that the output from earlier is gone
        wait_line(n + 1, blank_state)
        self.assertNotIn('admin', b.text(line_sel(n + 1)))

        def select_line(sel, width):
            # Select line by dragging mouse
            # 10px padding on all sides
            # Height on a line is around 14px, so start approx. in the middle of line
            b.mouse(sel, "mousedown", 10, 17)
            b.mouse(sel, "mousemove", 10 + width, 17)
            b.mouse(sel, "mouseup", 10 + width, 17)

        # Firefox does not support setting of permissions
        # and therefore we cannot test copy/paste with context menu
        if b.cdp.browser != "firefox":
            b.grant_permissions("clipboardRead", "clipboardWrite")

            # Execute command
            wait_line(n, prompt)
            b.key_press('echo "XYZ"\r')
            wait_line(n + 1, "XYZ")

            sel = line_sel(n + 1)

            # Highlight 40px (3 letters, never wider that ~14px)
            select_line(sel, 40)

            # Right click and pick copy
            b.mouse(sel, "contextmenu", btn=2)
            b.click('.contextMenu .contextMenuOption:first-child')

            # Right click and pick paste
            b.mouse(sel, "contextmenu", btn=2)
            b.click('.contextMenu .contextMenuOption:nth-child(2)')

            # Wait for text to show up
            wait_line(n + 2, prompt + "XYZ")
            b.key_press('\r')
            b.wait_in_text(line_sel(n + 3), 'XYZ')

        # now reset terminal
        b.click('.btn:contains("Reset")')

        # assert that the output from earlier is gone
        wait_line(n + 1, blank_state)

        # Execute another command
        wait_line(n, prompt)
        b.key_press('echo "foo"\r')
        wait_line(n + 1, "foo")

        sel = line_sel(n + 1)
        # Highlight 40px (3 letters, never wider that ~14px)
        select_line(sel, 40)

        # Use keyboard shortcuts to copy text
        b.key_press(chr(45), 2, use_ord=True) # Ctrl + Insert
        b.key_press(chr(45), 8, use_ord=True) # Shift + Insert

        # Wait for text to show up
        wait_line(n + 2, prompt + "foo")

        # check that we get a sensible $PATH; this varies across OSes, so don't be too strict about it
        b.key_press('\rclear\recho $PATH > /tmp/path\r')
        # don't use wait_line() for the full match here, as line breaks get in the way; just wait until command has run
        wait_line(n + 1, prompt)
        path = m.execute("cat /tmp/path").strip()
        if m.ostree_image:
            self.assertIn("/usr/local/bin:/usr/bin", path)
        else:
            self.assertIn("/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", path)
        self.assertNotIn(":/.local", path)  # would happen with empty $HOME

        def check_theme_select(name, style):
            b.select_from_dropdown("#theme-select", name)
            b.wait_attr(".xterm-viewport", "style", style)

        # Check that color theme changes
        white_style = "background-color: rgb(255, 255, 255);"

        check_theme_select("Black", "background-color: rgb(0, 0, 0);")
        check_theme_select("Dark", "background-color: rgb(0, 43, 54);")
        check_theme_select("Light", "background-color: rgb(253, 246, 227);")
        check_theme_select("White", white_style)

        # Relogin and white-style should be remembered
        # Relogin on different page, as reloging on terminal prompts asking if you want to leave the site
        b.go("/system")
        b.relogin("/system")
        b.go("/system/terminal")
        b.enter_page("/system/terminal")
        b.wait_present('.terminal')
        b.wait_attr(".xterm-viewport", "style", white_style)


if __name__ == '__main__':
    test_main()
